package org.qiyu.live.msg.provider.service.impl;

import com.cloopen.rest.sdk.BodyType;
import com.cloopen.rest.sdk.CCPRestSmsSDK;
import jakarta.annotation.Resource;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.idea.qiyu.live.framework.redis.starter.key.MsgProviderCacheKeyBuilder;
import org.qiyu.live.msg.dto.MsgCheckDTO;
import org.qiyu.live.msg.enums.MsgSendResultEnum;
import org.qiyu.live.msg.provider.config.ApplicationProperties;
import org.qiyu.live.msg.provider.config.SmsTemplateIDEnum;
import org.qiyu.live.msg.provider.config.ThreadPoolManager;
import org.qiyu.live.msg.provider.dao.mapper.ISmsMapper;
import org.qiyu.live.msg.provider.dao.po.SmsPO;
import org.qiyu.live.msg.provider.service.ISmsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sfx
 * Date: 2023-10-01
 * Time: 19:18
 */
@Service
public class ISmsServiceImpl implements ISmsService {
    // 引入日志
    private Logger logger = LoggerFactory.getLogger(ISmsServiceImpl.class);

    @Resource
    private ApplicationProperties applicationProperties;

    @Value("${spring.cloud.nacos.config.namespace}")
    private String namespace;

    @Resource
    private MsgProviderCacheKeyBuilder cacheKeyBuilder;

    // 引入redis
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 引入mapper
    @Resource
    private ISmsMapper smsMapper;

    @Override
    public MsgSendResultEnum sendLoginCode(String phone) {
        // 1. 校验手机号是否合法(格式,是否为空...)
        if (StringUtils.isEmpty(phone)) {
            // 手机号为空
            logger.error("手机号为空，phone is{}", phone);
            return MsgSendResultEnum.SEND_MSG_PARAM_ERROR;
        }

        // 2. 生成短信验证码
        int code = RandomUtils.nextInt(1000, 9999);
        String key = cacheKeyBuilder.buildSMSKey(phone);
        // 2.1 防止同一个手机号重复登陆
        if (Boolean.TRUE.equals(redisTemplate.hasKey(key))) {
            logger.error("短期内同一个手机号不能发送太多次，phone is{}", phone);
            // 证明在有效期内,再一次发送验证码,我们直接返回失败
            return MsgSendResultEnum.SEND_MSG_CODE_FAIL;
        }

        // 2.2 给验证码设置一个有效期,在规定时间内不能重复发送(比如60s内不能再次发送)
        // 设置60s过期时间
        redisTemplate.opsForValue().set(key, code, 60, TimeUnit.SECONDS);

        // 3. 发送短信验证码
        // 因为我们要调用第三方发短信的平台,所以肯定会有延迟,我们为了不阻塞主线程去执行
        // 所以我们使用线程池去执行
        ThreadPoolManager.commonAsyncPool.execute(() -> {
            boolean isSuccess = sendSmsToCCP(phone, code);
            if (isSuccess) {
                logger.info("发送短信验证码成功 phone is{} code is{}", phone, code);
                // 发送成功,插入到表中一条记录
                insertOne(phone, code);
            }
        });

        return MsgSendResultEnum.SEND_MSG_CODE_SUCCESS;
    }

    /**
     * 给容联云平台发送短信操作
     *
     * @param phone
     * @param code
     */
    private boolean sendSmsToCCP(String phone, Integer code) {
        //测试环境就不发送短信了，节省话费
        if (namespace.contains("test")) {
            return true;
        }
        logger.info("正在发送短信phone is {},code is {}", phone, code);
        try {
            //生产环境请求地址：app.cloopen.com
            String serverIp = applicationProperties.getSmsServerIp();
            //请求端口
            String serverPort = String.valueOf(applicationProperties.getPort());
            //主账号,登陆云通讯网站后,可在控制台首页看到开发者主账号ACCOUNT SID和主账号令牌AUTH TOKEN
            String accountSId = applicationProperties.getAccountSId();
            String accountToken = applicationProperties.getAccountToken();
            //请使用管理控制台中已创建应用的APPID
            String appId = applicationProperties.getAppId();
            CCPRestSmsSDK sdk = new CCPRestSmsSDK();
            sdk.init(serverIp, serverPort);
            sdk.setAccount(accountSId, accountToken);
            sdk.setAppId(appId);
            sdk.setBodyType(BodyType.Type_JSON);
            // 测试账号，所有短信都会往这里发送
            String to = applicationProperties.getTestPhone();
            // 模版
            String templateId = SmsTemplateIDEnum.SMS_LOGIN_CODE_TEMPLATE.getTemplateId();
            //测试开发支持的文案如下：【云通讯】您的验证码是{1}，请于{2}分钟内正确输入。其中{1}和{2}为短信模板参数。
            String[] datas = {String.valueOf(code), "1"};
            //可选 扩展码，四位数字 0~9999
            String subAppend = "1234";
            String reqId = UUID.randomUUID().toString();
            //可选 第三方自定义消息id，最大支持32位英文数字，同账号下同一自然天内不允许重复
            HashMap<String, Object> result = sdk.sendTemplateSMS(to, templateId, datas, subAppend, reqId);
            if ("000000".equals(result.get("statusCode"))) {
                //正常返回输出data包体信息（map）
                HashMap<String, Object> data = (HashMap<String, Object>) result.get("data");
                Set<String> keySet = data.keySet();
                for (String key : keySet) {
                    Object object = data.get(key);
                    logger.info("key is {},object is {}", key, object);
                }
            } else {
                //异常返回输出错误码和错误信息
                logger.error("错误码:{},错误信息:{}", result.get("statusCode"), result.get("statusMsg"));
                return false;
            }
            return true;
        } catch (Exception e) {
            logger.error("[sendSmsToCCP] error is ", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public MsgCheckDTO checkLoginCode(String phone, Integer code) {
        // 1. 校验手机号,验证码
        if (StringUtils.isEmpty(phone) || code == null || code < 1000 || code > 9999) {
            logger.error("手机号或验证码不合法 phone is {} code is {}", phone, code);
            return new MsgCheckDTO(false, "手机号或验证码不合法");
        }
        // 2. 校验redis的验证码
        // 2.1 得到redis的key
        String cacheKey = cacheKeyBuilder.buildSMSKey(phone);
        // 2.2 从redis中获取验证码
        Integer cacheCode = (Integer) redisTemplate.opsForValue().get(cacheKey);
        if (cacheCode == null || cacheCode < 1000 || cacheCode > 9999) {
            logger.error("验证码已经过期 phone is {} , code is {}", phone, code);
            return new MsgCheckDTO(false, "验证码已经过期");
        }
        // 3. redis存在并且没有过期,校验验证码是否正确
        if (cacheCode.equals(code)) {
            // 如果验证码校验成功,就从redis中删除了,避免重用验证码
            redisTemplate.delete(cacheKey);
            return new MsgCheckDTO(true, "验证码校验成功");
        }
        // 验证码校验错误
        return new MsgCheckDTO(false, "验证码错误");
    }

    @Override
    public void insertOne(String phone, Integer code) {
        SmsPO smsPO = new SmsPO();
        smsPO.setCode(code);
        smsPO.setPhone(phone);
        smsMapper.insert(smsPO);
    }
}